﻿using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Properties;

namespace Borg
{
    public class MonitorInfo : RepositoryElement
    {
        internal static int nextId = 1;

        internal static MonitorQualificationState unqualifiedState =
            MonitorQualificationState.QualificationMissing |
            MonitorQualificationState.NoMonitorEventHandlerFound;

        internal Dictionary<MethodInfo, ReflectionEventHandler> monitorEventHandler =
            new Dictionary<MethodInfo, ReflectionEventHandler>();

        internal MonitorQualificationState qualificationState;
        internal MonitorAttribute monitorAttribute;
        internal ExtensionTypeInfo extensionTypeInfo;

        internal MethodInfo OnContractAdded;
        internal MethodInfo OnContractRemoved;

        internal MethodInfo OnPluginAdded;
        internal MethodInfo OnPluginRemoved;

        internal MethodInfo OnExtensionCreating;
        internal MethodInfo OnExtensionCreated;

        internal MethodInfo OnExtensionReleasing;
        internal MethodInfo OnExtensionReleased;

        internal MethodInfo OnSlotOpening;
        internal MethodInfo OnSlotOpened;

        internal MethodInfo OnSlotClosing;
        internal MethodInfo OnSlotClosed;

        internal MethodInfo OnRegistering;
        internal MethodInfo OnRegistered;

        internal MethodInfo OnDeregistering;
        internal MethodInfo OnDeregistered;

        internal MethodInfo OnPlugging;
        internal MethodInfo OnPluggingCanceled;
        internal MethodInfo OnPlugged;

        internal MethodInfo OnUnplugging;
        internal MethodInfo OnUnplugged;

        internal MethodInfo OnSelectionChanged;

        internal MethodInfo OnTaskEnqueued;
        internal MethodInfo OnTaskDequeued;
        internal MethodInfo OnQueueEmptied;

        public IEnumerable<KeyValuePair<string, string>> EventHandlers
        {
            get
            {
                yield return new KeyValuePair<string, string>(
                    "OnContractAdded", monitorAttribute.OnContractAdded);
            }
        }

        public bool IsQualified
        {
            get { return (qualificationState & unqualifiedState) == MonitorQualificationState.None; }
        }

        public bool IsQualificationMissing
        {
            get
            {
                return
                    (qualificationState &
                    MonitorQualificationState.QualificationMissing) ==
                    MonitorQualificationState.QualificationMissing;
            }
        }

        public override string Name
        {
            get { return string.Empty; }
        }

        public override Assembly Assembly
        {
            get { return extensionTypeInfo.Assembly; }
        }

        static MonitorInfo()
        { }

        internal MonitorInfo(ExtensionTypeInfo extTypeInfo, MonitorAttribute attr)
        {
            id = nextId++;
            extensionTypeInfo = extTypeInfo;
            monitorAttribute = attr;
            qualificationState = MonitorQualificationState.QualificationMissing;
        }

        public MonitorQualificationState Qualify()
        {
            var s = string.Empty;

            Runtime.OnLogEvent(
                this,
                new LogEventArgs(
                    MessageType.Info,
                    VerbosityLevel.Diagnostic,
                    Messages.QualifyMonitor,
                    new object[]
                        {
                            extensionTypeInfo.id.ToString(CultureInfo.InvariantCulture),
                            extensionTypeInfo.Name
                        }));

            VerifyEventHandlerMethods();

            if ((qualificationState & MonitorQualificationState.NoMonitorEventHandlerFound) ==
                MonitorQualificationState.NoMonitorEventHandlerFound)
                s = Messages.NoMonitorEventHandlerMethodDefined;

            qualificationState &= ~MonitorQualificationState.QualificationMissing;

            if ((qualificationState & unqualifiedState) == MonitorQualificationState.None)
                Runtime.OnLogEvent(
                    this,
                    new LogEventArgs(
                        MessageType.Info,
                        VerbosityLevel.Diagnostic,
                        Messages.QualificationMonitor,
                        new object[]
                            {
                                extensionTypeInfo.id.ToString(CultureInfo.InvariantCulture),
                                extensionTypeInfo.Name,
                                Messages.QualifyResultOk,
                                s
                            }));
            else
                Runtime.OnLogEvent(
                    this,
                    new LogEventArgs(
                        MessageType.Error,
                        VerbosityLevel.Normal,
                        Messages.QualificationMonitor,
                        new object[]
                            {
                                extensionTypeInfo.id.ToString(CultureInfo.InvariantCulture),
                                extensionTypeInfo.Name,
                                Messages.QualifyResultFailed,
                                string.Format("\n  {0}", s)
                            }));

            return qualificationState;
        }

        internal MonitorQualificationState VerifyEventHandlerMethods()
        {
            var type = extensionTypeInfo.type;
            var name = extensionTypeInfo.Name;
            var status = true;

            if (monitorAttribute.OnContractAdded != string.Empty)
            {
                OnContractAdded =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnContractAdded,
                        Runtime.contractEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnContractAdded",
                            name));

                if (OnContractAdded == null)
                    qualificationState |= MonitorQualificationState.OnContractAddedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnContractRemoved != string.Empty)
            {
                OnContractRemoved =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnContractRemoved,
                        Runtime.contractEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnContractRemoved",
                            name));

                if (OnContractRemoved == null)
                    qualificationState |= MonitorQualificationState.OnContractRemovedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnPluginAdded != string.Empty)
            {
                OnPluginAdded =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnPluginAdded,
                        Runtime.pluginEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnPluginAdded",
                            name));

                if (OnPluginAdded == null)
                    qualificationState |= MonitorQualificationState.OnPluginAddedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnPluginRemoved != string.Empty)
            {
                OnPluginRemoved =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnPluginRemoved,
                        Runtime.pluginEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnPluginRemoved",
                            name));

                if (OnPluginRemoved == null)
                    qualificationState |= MonitorQualificationState.OnPluginRemovedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnExtensionCreating != string.Empty)
            {
                OnExtensionCreating =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnExtensionCreating,
                        Runtime.cancelExtensionEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnExtensionCreating",
                            name));

                if (OnExtensionCreating == null)
                    qualificationState |= MonitorQualificationState.OnExtensionCreatingNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnExtensionCreated != string.Empty)
            {
                OnExtensionCreated =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnExtensionCreated,
                        Runtime.extensionEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnExtensionCreated",
                            name));

                if (OnExtensionCreated == null)
                    qualificationState |= MonitorQualificationState.OnExtensionCreatedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnExtensionReleasing != string.Empty)
            {
                OnExtensionReleasing =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnExtensionReleasing,
                        Runtime.cancelExtensionEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnExtensionReleasing",
                            name));

                if (OnExtensionReleasing == null)
                    qualificationState |= MonitorQualificationState.OnExtensionReleasingNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnExtensionReleased != string.Empty)
            {
                OnExtensionReleased =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnExtensionReleased,
                        Runtime.extensionEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnExtensionReleased",
                            name));

                if (OnExtensionReleased == null)
                    qualificationState |= MonitorQualificationState.OnExtensionReleasedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnSlotOpening != string.Empty)
            {

                OnSlotOpening =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnSlotOpening,
                        Runtime.cancelSlotEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnSlotOpening",
                            name));

                if (OnSlotOpening == null)
                    qualificationState |= MonitorQualificationState.OnSlotOpeningNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnSlotOpened != string.Empty)
            {
                OnSlotOpened =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnSlotOpened,
                        Runtime.slotEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnSlotOpened",
                            name));

                if (OnSlotOpened == null)
                    qualificationState |= MonitorQualificationState.OnSlotOpenedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnSlotClosing != string.Empty)
            {
                OnSlotClosing =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnSlotClosing,
                        Runtime.cancelSlotEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnSlotClosing",
                            name));

                if (OnSlotClosing == null)
                    qualificationState |= MonitorQualificationState.OnSlotClosingNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnSlotClosed != string.Empty)
            {
                OnSlotClosed =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnSlotClosed,
                        Runtime.slotEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnSlotClosed",
                            name));

                if (OnSlotClosed == null)
                    qualificationState |= MonitorQualificationState.OnSlotClosedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnRegistering != string.Empty)
            {
                OnRegistering =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnRegistering,
                        Runtime.cancelRegisterEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnRegistering",
                            name));

                if (OnRegistering == null)
                    qualificationState |= MonitorQualificationState.OnRegisteringNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnRegistered != string.Empty)
            {
                OnRegistered =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                    type,
                    monitorAttribute.OnRegistered,
                    Runtime.registerEventHandlerParamTypes,
                    string.Format(
                    Messages.MonitorEventHandlerMethodDesc,
                    "OnRegistered",
                    name));

                if (OnRegistered == null)
                    qualificationState |= MonitorQualificationState.OnRegisteredNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnDeregistering != string.Empty)
            {
                OnDeregistering =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnDeregistering,
                        Runtime.cancelRegisterEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnDeregistering",
                            name));

                if (OnDeregistering == null)
                    qualificationState |= MonitorQualificationState.OnDeregisteringNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnDeregistered != string.Empty)
            {
                OnDeregistered =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnDeregistered,
                        Runtime.registerEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnDeregistered",
                            name));

                if (OnDeregistered == null)
                    qualificationState |= MonitorQualificationState.OnDeregisteredNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnPlugging != string.Empty)
            {
                OnPlugging =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnPlugging,
                        Runtime.cancelPlugEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnPlugging",
                            name));

                if (OnPlugging == null)
                    qualificationState |= MonitorQualificationState.OnPluggingNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnPluggingCanceled != string.Empty)
            {
                OnPluggingCanceled =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnPluggingCanceled,
                        Runtime.cancelPlugEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnPluggingCanceled",
                            name));

                if (OnPluggingCanceled == null)
                    qualificationState |= MonitorQualificationState.OnPluggingCanceledNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnPlugged != string.Empty)
            {
                OnPlugged =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnPlugged,
                        Runtime.plugEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnPlugged",
                            name));

                if (OnPlugged == null)
                    qualificationState |= MonitorQualificationState.OnPluggedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnUnplugging != string.Empty)
            {
                OnUnplugging =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnUnplugging,
                        Runtime.cancelPlugEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnUnplugging",
                            name));

                if (OnUnplugging == null)
                    qualificationState |= MonitorQualificationState.OnUnpluggingNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnUnplugged != string.Empty)
            {
                OnUnplugged =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnUnplugged,
                        Runtime.plugEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnUnplugged",
                            name));

                if (OnUnplugged == null)
                    qualificationState |= MonitorQualificationState.OnUnpluggedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnSelectionChanged != string.Empty)
            {
                OnSelectionChanged =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnSelectionChanged,
                        Runtime.selectionChangedHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnSelectionChanged",
                            name));

                if (OnSelectionChanged == null)
                    qualificationState |= MonitorQualificationState.OnSelectionChangedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnTaskEnqueued != string.Empty)
            {
                OnTaskEnqueued =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnTaskEnqueued,
                        Runtime.taskEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnTaskEnqueued",
                            name));

                if (OnTaskEnqueued == null)
                    qualificationState |= MonitorQualificationState.OnTaskEnqueuedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnTaskDequeued != string.Empty)
            {
                OnTaskDequeued =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnTaskDequeued,
                        Runtime.taskEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnTaskDequeued",
                            name));

                if (OnTaskDequeued == null)
                    qualificationState |= MonitorQualificationState.OnTaskDequeuedNotFound;
                else
                    status = false;
            }

            if (monitorAttribute.OnQueueEmptied != string.Empty)
            {
                OnQueueEmptied =
                    ExtensionTypeInfo.VerifyEventHandlerMethod(
                        type,
                        monitorAttribute.OnQueueEmptied,
                        Runtime.taskEventHandlerParamTypes,
                        string.Format(
                            Messages.MonitorEventHandlerMethodDesc,
                            "OnQueueEmptied",
                            name));

                if (OnQueueEmptied == null)
                    qualificationState |= MonitorQualificationState.OnQueueEmptiedNotFound;
                else
                    status = false;
            }

            if (status)
                qualificationState |= MonitorQualificationState.NoMonitorEventHandlerFound;

            return qualificationState;
        }

        internal void Initialize(object extensionObject)
        {
            if (!IsQualified && IsQualificationMissing)
            {
                Qualify();

                if (!IsQualified)
                    return;
            }

            lock (Runtime.Repository.contracts)
            {
                if (OnContractAdded != null)
                {
                    var handler = new ReflectionEventHandler(extensionObject, OnContractAdded);
                    monitorEventHandler.Add(OnContractAdded, handler);

                    foreach (var ci in Runtime.Repository.contracts.Values)
                        handler.InvokeMethod(Runtime.Repository, new ContractEventArgs(ci, null));

                    Runtime.Repository.ContractAdded += handler.InvokeMethod;
                }

                if (OnContractRemoved != null)
                {
                    var handler = new ReflectionEventHandler(extensionObject, OnContractRemoved);
                    monitorEventHandler.Add(OnContractRemoved, handler);
                    Runtime.Repository.ContractRemoved += handler.InvokeMethod;
                }
            }

            if (OnPluginAdded != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnPluginAdded);
                monitorEventHandler.Add(OnPluginAdded, handler);

                foreach (var pluginInfo in Runtime.Repository.plugins.Values)
                    handler.InvokeMethod(Runtime.Repository, new PluginEventArgs(pluginInfo, null));

                Runtime.Repository.PluginAdded += handler.InvokeMethod;
            }

            if (OnPluginRemoved != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnPluginRemoved);
                monitorEventHandler.Add(OnPluginRemoved, handler);
                Runtime.Repository.PluginRemoved += handler.InvokeMethod;
            }

            if (OnExtensionCreating != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnExtensionCreating);
                monitorEventHandler.Add(OnExtensionCreating, handler);

                foreach (var eti in Runtime.Repository.ExtensionTypeInfos)
                    eti.ExtensionCreating += handler.InvokeMethod;
            }

            if (OnExtensionCreated != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnExtensionCreated);
                monitorEventHandler.Add(OnExtensionCreated, handler);

                foreach (var eti in Runtime.Repository.ExtensionTypeInfos)
                {
                    foreach (var ei in eti.extensionInfos)
                        handler.InvokeMethod(eti, new ExtensionEventArgs(ei, null));

                    eti.ExtensionCreated += handler.InvokeMethod;
                }
            }

            if (OnExtensionReleasing != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnExtensionReleasing);
                monitorEventHandler.Add(OnExtensionReleasing, handler);

                foreach (var eti in Runtime.Repository.ExtensionTypeInfos)
                    eti.ExtensionReleasing += handler.InvokeMethod;
            }

            if (OnExtensionReleased != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnExtensionReleased);
                monitorEventHandler.Add(OnExtensionReleased, handler);

                foreach (var eti in Runtime.Repository.ExtensionTypeInfos)
                    eti.ExtensionReleased += handler.InvokeMethod;
            }

            if (OnSlotOpening != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnSlotOpening);
                monitorEventHandler.Add(OnSlotOpening, handler);

                foreach (var slotTypeInfo in Runtime.Repository.SlotTypeInfos)
                    slotTypeInfo.Opening += handler.InvokeMethod;
            }

            if (OnSlotOpened != null)
            {
                var reflectionEventHandler = new ReflectionEventHandler(extensionObject, OnSlotOpened);
                monitorEventHandler.Add(OnSlotOpened, reflectionEventHandler);

                foreach (var eti in Runtime.Repository.ExtensionTypeInfos)
                {
                    foreach (var si in from ei in eti.extensionInfos from si in ei.slotInfos where si.isOpen select si)
                        reflectionEventHandler.InvokeMethod(si, new SlotEventArgs(si, null));

                    foreach (var sti in eti.slotTypeInfos)
                        sti.Opened += reflectionEventHandler.InvokeMethod;
                }
            }

            if (OnSlotClosing != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnSlotClosing);
                monitorEventHandler.Add(OnSlotClosing, handler);

                foreach (var si in Runtime.Repository.SlotTypeInfos)
                    si.Closing += handler.InvokeMethod;
            }

            if (OnSlotClosed != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnSlotClosed);
                monitorEventHandler.Add(OnSlotClosed, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Closed += handler.InvokeMethod;
            }

            if (OnRegistering != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnRegistering);
                monitorEventHandler.Add(OnRegistering, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Registering += handler.InvokeMethod;
            }

            if (OnRegistered != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnRegistered);
                monitorEventHandler.Add(OnRegistered, handler);

                foreach (var si in Runtime.Repository.SlotInfos)
                {
                    foreach (var pti in si.registeredPlugTypeInfos)
                        handler.InvokeMethod(si, new RegisterEventArgs(si, pti, null));
                }

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Registered += handler.InvokeMethod;
            }

            if (OnDeregistering != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnDeregistering);
                monitorEventHandler.Add(OnDeregistering, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Deregistering += handler.InvokeMethod;
            }

            if (OnDeregistered != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnDeregistered);
                monitorEventHandler.Add(OnDeregistered, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Deregistered += handler.InvokeMethod;
            }

            if (OnPlugging != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnPlugging);
                monitorEventHandler.Add(OnPlugging, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Plugging += handler.InvokeMethod;
            }

            if (OnPluggingCanceled != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnPluggingCanceled);
                monitorEventHandler.Add(OnPluggingCanceled, handler);

                foreach (SlotTypeInfo sti in Runtime.Repository.SlotTypeInfos)
                    sti.PluggingCanceled += handler.InvokeMethod;
            }

            if (OnPlugged != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnPlugged);
                monitorEventHandler.Add(OnPlugged, handler);

                foreach (var si in Runtime.Repository.SlotInfos)
                {
                    foreach (var pi in si.pluggedPlugInfos)
                        handler.InvokeMethod(si, new PlugEventArgs(si, pi, null));
                }

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Plugged += handler.InvokeMethod;
            }

            if (OnUnplugging != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnUnplugging);
                monitorEventHandler.Add(OnUnplugging, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Unplugging += handler.InvokeMethod;
            }

            if (OnUnplugged != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnUnplugged);
                monitorEventHandler.Add(OnUnplugged, handler);

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.Unplugged += handler.InvokeMethod;
            }

            if (OnSelectionChanged != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnSelectionChanged);
                monitorEventHandler.Add(OnSelectionChanged, handler);

                foreach (var slot in Runtime.Repository.SlotInfos.Where(slot => slot.SelectedPlugs.Count > 0))
                    handler.InvokeMethod(
                        slot,
                        new SelectionChangedEventArgs(
                            slot,
                            slot.SelectedPlugs,
                            new List<PlugInfo>()));

                foreach (var sti in Runtime.Repository.SlotTypeInfos)
                    sti.SelectionChanged += handler.InvokeMethod;
            }

            if (OnTaskEnqueued != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnTaskEnqueued);
                monitorEventHandler.Add(OnTaskEnqueued, handler);

                foreach (var task in Runtime.TaskQueue.QueuedTasks)
                    handler.InvokeMethod(Runtime.TaskQueue, new TaskEventArgs(task, null));

                Runtime.TaskQueue.TaskEnqueued += handler.InvokeMethod;
            }

            if (OnTaskDequeued != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnTaskDequeued);
                monitorEventHandler.Add(OnTaskDequeued, handler);
                Runtime.TaskQueue.TaskDequeued += handler.InvokeMethod;
            }

            if (OnQueueEmptied != null)
            {
                var handler = new ReflectionEventHandler(extensionObject, OnQueueEmptied);
                monitorEventHandler.Add(OnQueueEmptied, handler);
                Runtime.TaskQueue.QueueEmptied += handler.InvokeMethod;
            }

            Runtime.Repository.monitors.Add(this);
        }

        internal void Release()
        {
            Runtime.Repository.monitors.Remove(this);

            if (OnContractAdded != null)
                Runtime.Repository.ContractAdded -= monitorEventHandler[OnContractAdded].InvokeMethod;

            if (OnContractRemoved != null)
                Runtime.Repository.ContractRemoved -= monitorEventHandler[OnContractRemoved].InvokeMethod;

            if (OnPluginAdded != null)
                Runtime.Repository.PluginAdded -= monitorEventHandler[OnPluginAdded].InvokeMethod;

            if (OnPluginRemoved != null)
                Runtime.Repository.PluginRemoved -= monitorEventHandler[OnPluginRemoved].InvokeMethod;

            lock (Runtime.Repository.plugins)
                foreach (var pi in Runtime.Repository.plugins.Values)
                    UnsubscribePluginInfo(pi);

            if (OnTaskEnqueued != null)
                Runtime.TaskQueue.TaskEnqueued -= monitorEventHandler[OnTaskEnqueued].InvokeMethod;

            if (OnTaskDequeued != null)
                Runtime.TaskQueue.TaskDequeued -= monitorEventHandler[OnTaskDequeued].InvokeMethod;

            if (OnQueueEmptied != null)
                Runtime.TaskQueue.QueueEmptied -= monitorEventHandler[OnQueueEmptied].InvokeMethod;

            monitorEventHandler.Clear();
        }

        internal void SubscribePluginInfo(PluginInfo pluginInfo)
        {
            if (!IsQualified && IsQualificationMissing)
            {
                Qualify();

                if (!IsQualified)
                    return;
            }

            foreach (var eti in pluginInfo.extensionTypeInfos)
            {
                if (OnExtensionCreating != null)
                    eti.ExtensionCreating += monitorEventHandler[OnExtensionCreating].InvokeMethod;
                if (OnExtensionCreated != null)
                    eti.ExtensionCreated += monitorEventHandler[OnExtensionCreated].InvokeMethod;

                if (OnExtensionReleasing != null)
                    eti.ExtensionReleasing += monitorEventHandler[OnExtensionReleasing].InvokeMethod;

                if (OnExtensionReleased != null)
                    eti.ExtensionReleased += monitorEventHandler[OnExtensionReleased].InvokeMethod;

                foreach (var sti in eti.slotTypeInfos)
                {
                    if (OnSlotOpening != null)
                        sti.Opening += monitorEventHandler[OnSlotOpening].InvokeMethod;

                    if (OnSlotOpened != null)
                        sti.Opened += monitorEventHandler[OnSlotOpened].InvokeMethod;

                    if (OnSlotClosing != null)
                        sti.Closing += monitorEventHandler[OnSlotClosing].InvokeMethod;

                    if (OnSlotClosed != null)
                        sti.Closed += monitorEventHandler[OnSlotClosed].InvokeMethod;

                    if (OnRegistering != null)
                        sti.Registering += monitorEventHandler[OnRegistering].InvokeMethod;

                    if (OnRegistered != null)
                        sti.Registered += monitorEventHandler[OnRegistered].InvokeMethod;

                    if (OnDeregistering != null)
                        sti.Deregistering += monitorEventHandler[OnDeregistering].InvokeMethod;

                    if (OnDeregistered != null)
                        sti.Deregistered += monitorEventHandler[OnDeregistered].InvokeMethod;

                    if (OnPlugging != null)
                        sti.Plugging += monitorEventHandler[OnPlugging].InvokeMethod;

                    if (OnPluggingCanceled != null)
                        sti.PluggingCanceled += monitorEventHandler[OnPluggingCanceled].InvokeMethod;

                    if (OnPlugged != null)
                        sti.Plugged += monitorEventHandler[OnPlugged].InvokeMethod;

                    if (OnUnplugging != null)
                        sti.Unplugging += monitorEventHandler[OnUnplugging].InvokeMethod;

                    if (OnUnplugged != null)
                        sti.Unplugged += monitorEventHandler[OnUnplugged].InvokeMethod;

                    if (OnSelectionChanged != null)
                        sti.SelectionChanged += monitorEventHandler[OnSelectionChanged].InvokeMethod;
                }
            }
        }

        internal void UnsubscribePluginInfo(PluginInfo pluginInfo)
        {
            if (!IsQualified && IsQualificationMissing)
            {
                Qualify();

                if (!IsQualified)
                    return;
            }

            foreach (var eti in pluginInfo.extensionTypeInfos)
            {
                if (OnExtensionCreating != null)
                    eti.ExtensionCreating -= monitorEventHandler[OnExtensionCreating].InvokeMethod;
                if (OnExtensionCreated != null)
                    eti.ExtensionCreated -= monitorEventHandler[OnExtensionCreated].InvokeMethod;
                if (OnExtensionReleasing != null)
                    eti.ExtensionReleasing -= monitorEventHandler[OnExtensionReleasing].InvokeMethod;
                if (OnExtensionReleased != null)
                    eti.ExtensionReleased -= monitorEventHandler[OnExtensionReleased].InvokeMethod;

                foreach (var sti in eti.slotTypeInfos)
                {
                    if (OnSlotOpening != null)
                        sti.Opening -= monitorEventHandler[OnSlotOpening].InvokeMethod;

                    if (OnSlotOpened != null)
                        sti.Opened -= monitorEventHandler[OnSlotOpened].InvokeMethod;

                    if (OnSlotClosing != null)
                        sti.Closing -= monitorEventHandler[OnSlotClosing].InvokeMethod;

                    if (OnSlotClosed != null)
                        sti.Closed -= monitorEventHandler[OnSlotClosed].InvokeMethod;

                    if (OnRegistering != null)
                        sti.Registering -= monitorEventHandler[OnRegistering].InvokeMethod;

                    if (OnRegistered != null)
                        sti.Registered -= monitorEventHandler[OnRegistered].InvokeMethod;

                    if (OnDeregistering != null)
                        sti.Deregistering -= monitorEventHandler[OnDeregistering].InvokeMethod;

                    if (OnDeregistered != null)
                        sti.Deregistered -= monitorEventHandler[OnDeregistered].InvokeMethod;

                    if (OnPlugging != null)
                        sti.Plugging -= monitorEventHandler[OnPlugging].InvokeMethod;

                    if (OnPluggingCanceled != null)
                        sti.PluggingCanceled -= monitorEventHandler[OnPluggingCanceled].InvokeMethod;

                    if (OnPlugged != null)
                        sti.Plugged -= monitorEventHandler[OnPlugged].InvokeMethod;

                    if (OnUnplugging != null)
                        sti.Unplugging -= monitorEventHandler[OnUnplugging].InvokeMethod;

                    if (OnUnplugged != null)
                        sti.Unplugged -= monitorEventHandler[OnUnplugged].InvokeMethod;

                    if (OnSelectionChanged != null)
                        sti.SelectionChanged -= monitorEventHandler[OnSelectionChanged].InvokeMethod;
                }
            }
        }
    }
}

